// Package health provides health check functionality for the WePayKit application.
// It implements endpoints to check the status of various system components.
package health

import (
    "context"
    "encoding/json"
    "net/http"
    "sync"
    "time"

    "github.com/we-pay-kit/we-pay-kit/internal/cache"
    "github.com/we-pay-kit/we-pay-kit/internal/db"
    "github.com/we-pay-kit/we-pay-kit/internal/mq"
)

// Status represents the health status of a component
type Status string

const (
	// StatusUp indicates the component is functioning properly
	StatusUp Status = "UP"
	// StatusDown indicates the component is not functioning properly
	StatusDown Status = "DOWN"
	// StatusUnknown indicates the component status is unknown
	StatusUnknown Status = "UNKNOWN"
)

// Check represents a health check for a specific component
type Check struct {
	Name      string    `json:"name"`
	Status    Status    `json:"status"`
	Message   string    `json:"message,omitempty"`
	Timestamp time.Time `json:"timestamp"`
}

// Response represents the overall health check response
type Response struct {
	Status    Status    `json:"status"`
	Timestamp time.Time `json:"timestamp"`
	Checks    []Check   `json:"checks"`
}

// Checker defines the interface for health checkers
type Checker interface {
	Name() string
	Check() (Status, string)
}

// Service manages health checks for all components
type Service struct {
	checkers []Checker
	mutex    sync.RWMutex
}

// NewService creates a new health check service
func NewService() *Service {
	return &Service{
		checkers: make([]Checker, 0),
	}
}

// Register adds a new health checker to the service
func (s *Service) Register(checker Checker) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.checkers = append(s.checkers, checker)
}

// RegisterDefaults registers default health checkers for database, cache, and message queue
func (s *Service) RegisterDefaults() {
	// Register database checker
	s.Register(&DatabaseChecker{})
	
	// Register cache checker
	s.Register(&CacheChecker{})
	
	// Register message queue checker
	s.Register(&MessageQueueChecker{})
}

// Handler returns an HTTP handler for health checks
func (s *Service) Handler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		response := s.Check()
		
		// Set content type
		w.Header().Set("Content-Type", "application/json")
		
		// Set status code based on overall health
		if response.Status == StatusUp {
			w.WriteHeader(http.StatusOK)
		} else {
			w.WriteHeader(http.StatusServiceUnavailable)
		}
		
		// Encode and send response
		if err := json.NewEncoder(w).Encode(response); err != nil {
			http.Error(w, "Failed to encode response", http.StatusInternalServerError)
			return
		}
	}
}

// Check performs all registered health checks and returns the results
func (s *Service) Check() Response {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	
	response := Response{
		Status:    StatusUp,
		Timestamp: time.Now(),
		Checks:    make([]Check, len(s.checkers)),
	}
	
	// Perform all checks concurrently
	var wg sync.WaitGroup
	checkResults := make(chan Check, len(s.checkers))
	
    for _, checker := range s.checkers {
		wg.Add(1)
		go func(c Checker) {
			defer wg.Done()

            // 对每个检查设置超时，避免单点阻塞整体
            ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
            defer cancel()
            status, message := runWithTimeout(ctx, c)
			checkResults <- Check{
				Name:      c.Name(),
				Status:    status,
				Message:   message,
				Timestamp: time.Now(),
			}
		}(checker)
	}
	
	// Close the channel once all checks are done
	go func() {
		wg.Wait()
		close(checkResults)
	}()
	
	// Collect results
	i := 0
	for check := range checkResults {
		response.Checks[i] = check
		// If any check is down, mark overall status as down
		if check.Status == StatusDown {
			response.Status = StatusDown
		}
		i++
	}
	
	return response
}

// runWithTimeout 在上下文超时控制下执行单个检查器
func runWithTimeout(ctx context.Context, c Checker) (Status, string) {
    type result struct{
        s Status
        m string
    }
    ch := make(chan result, 1)
    go func() {
        s, m := c.Check()
        ch <- result{s: s, m: m}
    }()
    select {
    case res := <-ch:
        return res.s, res.m
    case <-ctx.Done():
        return StatusUnknown, "check timeout"
    }
}

// DatabaseChecker implements a health checker for the database
type DatabaseChecker struct{}

// Name returns the name of the database checker
func (c *DatabaseChecker) Name() string {
	return "database"
}

// Check performs the database health check
func (c *DatabaseChecker) Check() (Status, string) {
	// Get database instance
	database := db.GetDB()
	if database == nil {
		return StatusDown, "Database not initialized"
	}
	
	// Ping the database
	if err := database.Ping(); err != nil {
		return StatusDown, "Database ping failed: " + err.Error()
	}
	
	return StatusUp, "Database connection is healthy"
}

// CacheChecker implements a health checker for the cache
type CacheChecker struct{}

// Name returns the name of the cache checker
func (c *CacheChecker) Name() string {
	return "cache"
}

// Check performs the cache health check
func (c *CacheChecker) Check() (Status, string) {
	// Get cache instance
	cacheClient := cache.GetRedis()
	if cacheClient == nil {
        return StatusUnknown, "Cache not initialized"
	}
	
	// Ping the cache
    // 使用带命名空间与短时过期的 SetNX，避免污染
    _, err := cacheClient.SetNX("health:check", "ok", time.Second*5)
	if err != nil {
		return StatusDown, "Cache set failed: " + err.Error()
	}
	
    _, err = cacheClient.Get("health:check")
	if err != nil {
		return StatusDown, "Cache get failed: " + err.Error()
	}
	
	return StatusUp, "Cache connection is healthy"
}

// MessageQueueChecker implements a health checker for the message queue
type MessageQueueChecker struct{}

// Name returns the name of the message queue checker
func (c *MessageQueueChecker) Name() string {
	return "message_queue"
}

// Check performs the message queue health check
func (c *MessageQueueChecker) Check() (Status, string) {
	// Get message queue instance
	mqClient := mq.GetRabbitMQ()
	if mqClient == nil {
        return StatusUnknown, "Message queue not initialized"
	}
	
	// Try to declare a test queue to verify connection
	err := mqClient.DeclareQueue("health_check_queue")
	if err != nil {
		return StatusDown, "Message queue declare failed: " + err.Error()
	}
	
	return StatusUp, "Message queue connection is healthy"
}